<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Elliptical Drag with GSAP and SVG</title>
    <style>
        svg {
            display: block;
            margin: 20px auto;
        }

        .controls {
            display: flex;
            flex-wrap: wrap;
            justify-content: center;
            gap: 10px;
            margin: 20px;
        }

        .control-group {
            display: flex;
            align-items: center;
            gap: 5px;
        }

        .node {
            cursor: grab;
            user-select: none;
        }

        .node:active {
            cursor: grabbing;
        }

        .node-text {
            font-family: Arial, sans-serif;
            font-size: 5px;
            text-anchor: middle;
            dominant-baseline: middle;
            fill: white;
            user-select: none;
        }
    </style>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.9.1/gsap.min.js"></script>
</head>

<body>
    <div class="controls">
        <div class="control-group">
            <label for="cx">椭圆中心X:</label>
            <input type="number" id="cx" value="100" min="0" max="200" step="1">
        </div>
        <div class="control-group">
            <label for="cy">椭圆中心Y:</label>
            <input type="number" id="cy" value="50" min="0" max="100" step="1">
        </div>
        <div class="control-group">
            <label for="rx">椭圆长轴:</label>
            <input type="number" id="rx" value="80" min="10" max="100" step="1">
        </div>
        <div class="control-group">
            <label for="ry">椭圆短轴:</label>
            <input type="number" id="ry" value="25" min="10" max="50" step="1">
        </div>
        <div class="control-group">
            <label for="rotate">倾斜角度:</label>
            <input type="number" id="rotate" value="-6" step="1">
        </div>
        <button id="addNode">添加节点</button>
        <button id="resetNodes">重置</button>
    </div>

    <svg width="1000" height="500" viewBox="0 0 200 100">
        <g id="ellipseGroup">
            <ellipse id="ellipse" cx="100" cy="50" rx="80" ry="25" stroke="#3f6ffc" stroke-width="2" fill="none" />
        </g>
        <g id="nodesGroup">
            <circle cx="99" cy="25" r="7" fill="#3f6ffc" id="node-1" class="node" />
            <text x="99" y="25" class="node-text">2023</text>
        </g>
    </svg>

    <script>
        const svg = document.querySelector('svg');
        const ellipseGroup = document.getElementById('ellipseGroup');
        const ellipse = document.getElementById('ellipse');
        const nodesGroup = document.getElementById('nodesGroup');
        const addNodeBtn = document.getElementById('addNode');
        const resetBtn = document.getElementById('resetNodes');

        // 获取输入元素
        const cxInput = document.getElementById('cx');
        const cyInput = document.getElementById('cy');
        const rxInput = document.getElementById('rx');
        const ryInput = document.getElementById('ry');
        const rotateInput = document.getElementById('rotate');

        // 初始化参数（从输入框获取初始值）
        let ellipseCX = parseInt(cxInput.value);
        let ellipseCY = parseInt(cyInput.value);
        let ellipseRX = parseInt(rxInput.value);
        let ellipseRY = parseInt(ryInput.value);
        let ellipseRotate = parseInt(rotateInput.value);

        let nodes = []; // 存储所有节点
        let draggingNode = null; // 当前拖拽的节点
        let initialAngle = 0; // 初始拖拽角度
        let startAngle = 0; // 起始角度

        // 初始设置椭圆旋转角度
        ellipseGroup.setAttribute('transform', `rotate(${ellipseRotate} ${ellipseCX} ${ellipseCY})`);

        // 输入变化时更新椭圆参数
        cxInput.addEventListener('input', updateEllipseParams);
        cyInput.addEventListener('input', updateEllipseParams);
        rxInput.addEventListener('input', updateEllipseParams);
        ryInput.addEventListener('input', updateEllipseParams);
        rotateInput.addEventListener('input', updateEllipseParams);

        // 初始化现有节点
        document.querySelectorAll('.node').forEach(node => {
            nodes.push(node);
            setupDraggable(node);
        });

        // 添加新节点
        addNodeBtn.addEventListener('click', () => {
            const count = nodes.length + 1;
            const currentYear = new Date().getFullYear() + count - 1;

            // 创建节点组
            const nodeGroup = document.createElementNS("http://www.w3.org/2000/svg", "g");
            nodeGroup.setAttribute("id", `node-group-${count}`);

            // 创建节点圆
            const node = document.createElementNS("http://www.w3.org/2000/svg", "circle");
            node.setAttribute("class", "node");
            node.setAttribute("r", 7);
            node.setAttribute("fill", "#3f6ffc");
            node.setAttribute("id", `node-${count}`);
            nodeGroup.appendChild(node);

            // 创建节点文本
            const nodeText = document.createElementNS("http://www.w3.org/2000/svg", "text");
            nodeText.setAttribute("class", "node-text");
            nodeText.textContent = currentYear;
            nodeGroup.appendChild(nodeText);

            // 添加到SVG
            nodesGroup.appendChild(nodeGroup);

            nodes.push(node);
            setupDraggable(node);
            updateNodePositions();
        });

        // 重置节点
        resetBtn.addEventListener('click', () => {
            // 移除新增的节点
            while (nodes.length > 1) {
                const node = nodes.pop();
                const nodeGroup = node.parentNode;
                nodesGroup.removeChild(nodeGroup);
            }
            updateNodePositions();
        });

        function updateEllipseParams() {
            // 从输入框获取最新值
            ellipseCX = parseInt(cxInput.value);
            ellipseCY = parseInt(cyInput.value);
            ellipseRX = parseInt(rxInput.value);
            ellipseRY = parseInt(ryInput.value);
            ellipseRotate = parseInt(rotateInput.value);

            // 更新椭圆元素
            gsap.to(ellipse, {
                duration: 0.3,
                attr: {
                    cx: ellipseCX,
                    cy: ellipseCY,
                    rx: ellipseRX,
                    ry: ellipseRY
                }
            });

            // 更新椭圆组的旋转
            gsap.to(ellipseGroup, {
                duration: 0.3,
                rotation: ellipseRotate,
                transformOrigin: `${ellipseCX}px ${ellipseCY}px`
            });

            // 重新计算节点位置
            updateNodePositions();
        }

        // 设置节点拖拽功能
        function setupDraggable(node) {
            node.addEventListener('mousedown', (e) => {
                e.stopPropagation();
                draggingNode = node;

                // 获取节点在未旋转坐标系中的位置
                const pt = svg.createSVGPoint();
                pt.x = node.cx.baseVal.value;
                pt.y = node.cy.baseVal.value;

                // 应用逆旋转矩阵
                const rotateRad = -ellipseRotate * Math.PI / 180;
                const cos = Math.cos(rotateRad);
                const sin = Math.sin(rotateRad);

                // 相对于椭圆中心的坐标
                const relX = pt.x - ellipseCX;
                const relY = pt.y - ellipseCY;

                // 逆旋转后的坐标
                const unrotatedX = relX * cos - relY * sin;
                const unrotatedY = relX * sin + relY * cos;

                // 计算未旋转坐标系中的角度
                initialAngle = Math.atan2(unrotatedY, unrotatedX);

                // 记录开始拖拽时的角度
                const mousePt = svg.createSVGPoint();
                mousePt.x = e.clientX;
                mousePt.y = e.clientY;
                const cursorPt = mousePt.matrixTransform(svg.getScreenCTM().inverse());

                // 鼠标相对于椭圆中心的坐标
                const mouseRelX = cursorPt.x - ellipseCX;
                const mouseRelY = cursorPt.y - ellipseCY;

                // 应用逆旋转
                const mouseUnrotatedX = mouseRelX * cos - mouseRelY * sin;
                const mouseUnrotatedY = mouseRelX * sin + mouseRelY * cos;

                startAngle = Math.atan2(mouseUnrotatedY, mouseUnrotatedX);

                document.addEventListener('mousemove', onMouseMove);
                document.addEventListener('mouseup', onMouseUp);
            });
        }

        function onMouseMove(e) {
            if (!draggingNode) return;

            const pt = svg.createSVGPoint();
            pt.x = e.clientX;
            pt.y = e.clientY;
            const cursorPt = pt.matrixTransform(svg.getScreenCTM().inverse());

            // 鼠标相对于椭圆中心的坐标
            const mouseRelX = cursorPt.x - ellipseCX;
            const mouseRelY = cursorPt.y - ellipseCY;

            // 应用逆旋转矩阵
            const rotateRad = -ellipseRotate * Math.PI / 180;
            const cos = Math.cos(rotateRad);
            const sin = Math.sin(rotateRad);

            // 逆旋转后的坐标
            const mouseUnrotatedX = mouseRelX * cos - mouseRelY * sin;
            const mouseUnrotatedY = mouseRelX * sin + mouseRelY * cos;

            // 计算鼠标当前角度（在未旋转坐标系中）
            const currentAngle = Math.atan2(mouseUnrotatedY, mouseUnrotatedX);

            // 计算角度差
            let angleDiff = currentAngle - startAngle;

            // 处理角度跳跃问题
            if (angleDiff > Math.PI) angleDiff -= 2 * Math.PI;
            if (angleDiff < -Math.PI) angleDiff += 2 * Math.PI;

            // 新的基准角度（在未旋转坐标系中）
            const newBaseAngle = initialAngle + angleDiff;

            // 更新所有节点位置
            updateNodePositions(newBaseAngle);
        }

        function onMouseUp() {
            draggingNode = null;
            document.removeEventListener('mousemove', onMouseMove);
            document.removeEventListener('mouseup', onMouseUp);
        }

        // 更新所有节点位置，使它们在椭圆上均匀分布
        function updateNodePositions(baseAngle = 0) {
            const count = nodes.length;
            if (count === 0) return;

            // 计算相邻节点之间的角度间隔（弧度）
            const angleStep = (Math.PI * 2) / count;

            // 转换为弧度
            const rotateRad = ellipseRotate * Math.PI / 180;
            const cos = Math.cos(rotateRad);
            const sin = Math.sin(rotateRad);

            nodes.forEach((node, index) => {
                // 计算当前节点的角度（在未旋转坐标系中）
                const angle = baseAngle + (angleStep * index);

                // 在未旋转坐标系中的位置
                const unrotatedX = ellipseRX * Math.cos(angle);
                const unrotatedY = ellipseRY * Math.sin(angle);

                // 应用旋转变换
                const rotatedX = unrotatedX * cos - unrotatedY * sin;
                const rotatedY = unrotatedX * sin + unrotatedY * cos;

                // 相对于椭圆中心的最终位置
                const x = ellipseCX + rotatedX;
                const y = ellipseCY + rotatedY;

                // 使用GSAP平滑移动到新位置
                gsap.to(node, {
                    duration: 0.1,
                    attr: {
                        cx: x,
                        cy: y
                    }
                });

                // 更新对应文本位置
                const nodeGroup = node.parentNode;
                const nodeText = nodeGroup.querySelector('text');
                if (nodeText) {
                    gsap.to(nodeText, {
                        duration: 0.1,
                        attr: {
                            x: x,
                            y: y
                        }
                    });
                }
            });
        }
    </script>
</body>

</html>